package com.zwj.interview.缓存;

import java.util.HashMap;
import java.util.LinkedHashSet;

/**
 * @Author: Zwj
 * @Date: 2022/2/27 11:37
 */
public class LFUCache {

    HashMap<Integer, Integer> keyToVal;

    //key到频率freq的映射
    HashMap<Integer, Integer> keyToFreq;

    //频率freq到key的集合的映射，因为可能存在多个key的访问频率相同的情况
    HashMap<Integer, LinkedHashSet<Integer>> freqToKeys;

    //记录最小的频次
    int minFreq;

    int cap;

    public LFUCache(int capacity) {
        keyToVal = new HashMap<>();
        keyToFreq = new HashMap<>();
        freqToKeys = new HashMap<>();
        this.cap = capacity;
        this.minFreq = 0;
    }

    public int get(int key) {
        if (!keyToVal.containsKey(key)) {
            return -1;
        }
        increaseFreq(key);
        return keyToVal.get(key);
    }

    public void put(int key, int val) {
        if (this.cap <= 0) {
            return;
        }
        //如果key已经存在，那么更新值即可，增加key的频率
        if (keyToVal.containsKey(key)) {
            keyToVal.put(key, val);
            increaseFreq(key);
            return;
        }
        //容量已满的情况下，需要淘汰一个最小频率的key
        if (this.cap <= keyToVal.size()) {
            removeMinFreqKey();
        }
        //不存在该key的话，那么就直接添加
        keyToVal.put(key, val);
        //新增key-->freq的映射
        keyToFreq.put(key, 1);
        //新增这个freq到key的集合的映射
        freqToKeys.putIfAbsent(key, new LinkedHashSet<>());
        freqToKeys.get(1).add(key);
        //新增的key，那么minFreq肯定为1
        this.minFreq = 1;
    }

    //移除频率最小的key
    private void removeMinFreqKey() {
        //拿到最小频率的那些可以
        LinkedHashSet<Integer> keyList = freqToKeys.get(this.minFreq);
        //最先插入的可以就是要被淘汰的key
        int deletedKey = keyList.iterator().next();
        keyList.remove(deletedKey);
        if (keyList.isEmpty()) {
            freqToKeys.remove(this.minFreq);
        }
        keyToVal.remove(deletedKey);
        keyToFreq.remove(deletedKey);

    }

    //增加某个key的频率freq
    private void increaseFreq(int key) {
        int freq = keyToFreq.get(key);
        //更新key到freq的映射
        keyToFreq.put(key, freq + 1);
        //将key从freq对应的列表中删除
        freqToKeys.get(freq).remove(key);
        //将key加入freq+1对应的列表中
        freqToKeys.putIfAbsent(freq + 1, new LinkedHashSet<>());
        freqToKeys.get(freq + 1).add(key);
        //如果freq对应的列表空了，移除这个freq
        if (freqToKeys.get(freq).isEmpty()) {
            freqToKeys.remove(freq);
            //如果这个freq恰好就是minFreq,更新minFreq
            if (freq == this.minFreq) {
                this.minFreq++;
            }
        }


    }


}